from sqlalchemy.ext.asyncio import AsyncSession
# from sqlalchemy.exc import NoResultFound
from sqlalchemy.orm import selectinload
from sqlalchemy import select, update
from models.user import User, Role, Power
from typing import Optional
from . import RepositoryNotFoundExt

###########
## 数据库层
###########
__all__ = ["Repository"]

class Repository:
    def __init__(self, session: AsyncSession):
        self.sessinon = session
    async def get_user(self, user_id: int) -> Optional[User]:
        user = await self.sessinon.get(User, user_id)
        return user
    
    async def get_users(self, offset: int = 0, limit: int = 10) -> list[User]:
        query = select(User).offset(offset).limit(limit)
        result = await self.sessinon.execute(query)
        return result.scalars().all()
    
    async def update_user(self, user_id: int, update_values: dict) -> int:
        query = update(User).where(User.id == user_id).values(update_values)
        result = await self.sessinon.execute(query)
        return result.rowcount

    async def get_user_role_powers(self, user_id: int) -> User:
        query = select(User, Role, Power).where(User.id == user_id).options(selectinload(User.roles).selectinload(Role.powers))
        res = await self.sessinon.execute(query)
        user = res.scalar()
        return user

    async def has_perm(self, user_id: int, power_name: str) -> bool:
        user = await self.get_user(user_id)
        is_perm =  await user.has_perm(power_name, self.sessinon)
        return is_perm

    
    async def create_user(self, create_values: dict) -> User:
        user = User(**create_values)
        self.sessinon.add(user)
        await self.sessinon.commit()
        await self.sessinon.flush(user)
        return user
    
    async def get_user_roles(self, user_id: int) -> Optional[User]:
        query = select(User).where(User.id == user_id).options(selectinload(User.roles))
        res = await self.sessinon.execute(query)
        return res.scalar()

    async def user_append_role(self, user_id: int, role_id: int) -> User:
        role = await self.get_role(role_id)
        if role is None:
            raise RepositoryNotFoundExt(msg="role not found")
        user = await self.get_user_roles(user_id)
        if user is None:
            raise RepositoryNotFoundExt(msg="user not found")
        user.roles.append(role)

        await self.sessinon.commit()
        await self.sessinon.flush()
        return user

    async def get_role(self, role_id: int) -> Optional[Role]:
        role = await self.sessinon.get(Role, role_id)
        return role
    
    async def create_role(self, create_values: dict) -> Role:
        role = Role(**create_values)
        self.sessinon.add(role)
        await self.sessinon.commit()
        await self.sessinon.flush(role)
        return role
    
    async def get_role_powers(self, role_id: int) -> Role:
        query = select(Role).where(Role.id == role_id).options(selectinload(Role.powers))
        res = await self.sessinon.execute(query)
        return res.scalar()

    async def get_user_detail(self, user_id: int) -> User:
        select(User).where(User.id == user_id).join(User.roles)
        pass

    async def role_append_power(self, role_id: int, power_id: int) -> Role:
        power = await self.get_power(power_id)
        if power is None:
            raise RepositoryNotFoundExt(msg="power not found")
        
        role = await self.get_role_powers(role_id)
        if role is None:
            raise RepositoryNotFoundExt(msg="role not found")

        role.powers.append(power)
        await self.sessinon.commit()
        await self.sessinon.flush()
        return role
    
    async def get_power(self, power_id: int) -> Optional[Power]:
        power = await self.sessinon.get(Power, power_id)
        return power
    async def get_powers(self, offset: int = 0, limit: int = 10) -> list[Power]:
        query = select(Power).offset(offset).limit(limit)
        result = await self.sessinon.execute(query)
        return result.scalars().all()    
    
    async def create_power(self, create_values: dict) -> Power:
        power = Power(**create_values)
        self.sessinon.add(power)
        await self.sessinon.commit()
        await self.sessinon.flush(power)
        return power